package com.adv.ems.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.json.JSONUtil;
import com.adv.ems.constants.EmsConst;
import com.adv.ems.db.entity.*;
import com.adv.ems.enums.CollectPointDataTypeEnum;
import com.adv.ems.enums.EnergyDataOriginal;
import com.adv.ems.filters.FilterFactory;
import com.adv.ems.filters.original.dto.EnergyDataOriginalBeforeDTO;
import com.adv.ems.filters.original.dto.OriginalDealAfterDTO;
import com.adv.ems.manager.*;
import com.adv.ems.model.dto.OriginalDataDealConfigDTO;
import com.adv.ems.model.vo.ResultVO;
import com.adv.ems.service.OriginalDataDispatchEnergyDataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OriginalDataDispatchEnergyDataServiceImpl implements OriginalDataDispatchEnergyDataService, SmartInitializingSingleton {
    @Resource
    SystemConfigManager systemConfigManager;
    @Resource
    EnergyDataOriginalManager energyDataOriginalManager;
    @Resource
    CollectPointManager collectPointManager;
    @Resource
    EnergyDataCumulateManager energyDataCumulateManager;
    @Resource
    EnergyDataProcessManager energyDataProcessManager;
    @Resource
    FilterFactory<EnergyDataOriginalBeforeDTO, Boolean> beforeFilterFactory;
    @Resource
    FilterFactory<OriginalDealAfterDTO, Boolean> afterFilterFactory;

    @Override
    public ResultVO<Boolean> begin() {
        ResultVO<Boolean> resultVO;
        OriginalDataDealConfigDTO config = getOriginalDataDealConfig();
        log.info("原始数据加工------开始【{}】", config.getLastId());
        List<EnergyDataOriginalDO> data = energyDataOriginalManager.loadUnDealData(config.getLastId(), config.getPullDataSize());
        log.info("原始数据加工------数据条数【{}】", data.size());
        if (CollectionUtil.isNotEmpty(data)) {
            resultVO = handle(data, config);
        }else {
            resultVO = ResultVO.success(false);
        }
        log.info("原始数据加工------结束");
        return resultVO;
    }

    private ResultVO<Boolean> handle(List<EnergyDataOriginalDO> data, OriginalDataDealConfigDTO config) {
        Long fromId = data.get(0).getId();
        Long toId = data.get(data.size() - 1).getId();
        Map<Long, CollectPointDO> collectPointMap = collectPointManager.getAllMap();
        log.info("原始数据加工------开始ID【{}】，结束ID【{}】", fromId, toId);
        //数据校验
        List<EnergyDataOriginalBeforeDTO> readyData = data.stream().map(EnergyDataOriginalBeforeDTO::from).collect(Collectors.toList());
        log.info("原始数据加工------开始前置校验");
        ResultVO<Boolean> result = beforeFilterFactory.doBeforeFilter(readyData);
        List<EnergyDataProcessDO> process = new ArrayList<>(readyData.size());
        List<EnergyDataCumulateDO> cumulate = new ArrayList<>(readyData.size());
        if (result.isSuccess()) {
            log.info("原始数据加工------构建累积/过程数据");
            readyData.forEach(d -> buildProcessCumulateData(d, collectPointMap.get(d.getCollectPointId()), process, cumulate));
        }else {
            log.info("原始数据加工------前置校验异常，标记数据");
            energyDataOriginalManager.batchUpdateDealState(readyData.get(0).getId(), readyData.get(readyData.size() - 1).getId(), EnergyDataOriginal.ERROR.name(), result.getMsg());
        }
        OriginalDealAfterDTO dealAfter = new OriginalDealAfterDTO();
        dealAfter.setOriginal(readyData);
        dealAfter.setProcess(process);
        dealAfter.setCumulate(cumulate);
        dealAfter.setFromId(fromId);
        dealAfter.setToId(toId);
        dealAfter.setConfig(config);
        log.info("原始数据加工------开始后置校验");
        afterFilterFactory.doAfterFilter(ListUtil.toList(dealAfter));
        return ResultVO.success(true);
    }


    private void buildProcessCumulateData(EnergyDataOriginalBeforeDTO data, CollectPointDO point, List<EnergyDataProcessDO> process, List<EnergyDataCumulateDO> cumulate) {
        if (point == null) {
            return;
        }
        ResultVO<EnergyDataCumulateDO> result = buildAndSaveCumulateData(data, point);
        if (result.isSuccess()) {
            cumulate.add(result.getContent());
        }
        ResultVO<EnergyDataProcessDO> resultVO = buildAndSaveProcessData(data, point);
        if (resultVO.isSuccess()) {
            process.add(resultVO.getContent());
        }

    }

    private ResultVO<EnergyDataCumulateDO> buildAndSaveCumulateData(EnergyDataOriginalBeforeDTO data, CollectPointDO point) {
        if (point == null) {
            return ResultVO.error();
        }
        if (!CollectPointDataTypeEnum.CUMULATE.name().equals(point.getDataType())) {
            return ResultVO.error();
        }
        EnergyDataCumulateDO record = new EnergyDataCumulateDO();
        record.setOriginalId(data.getId());
        record.setCollectPointId(data.getCollectPointId());
        record.setOriValue(data.getColData());
        record.setRealValue(data.getRealValue());
        record.setColDate(data.getColDate());
        record.setCreateTime(new Date());
        energyDataCumulateManager.insertData(record);
        return ResultVO.success(record);
    }

    private ResultVO<EnergyDataProcessDO> buildAndSaveProcessData(EnergyDataOriginalBeforeDTO data, CollectPointDO point) {
        if (!CollectPointDataTypeEnum.PROCESS.name().equals(point.getDataType())) {
            return ResultVO.error();
        }
        EnergyDataProcessDO record = new EnergyDataProcessDO();
        record.setOriginalId(data.getId());
        record.setCollectPointId(data.getCollectPointId());
        record.setOriValue(data.getColData());
        record.setRealValue(data.getRealValue());
        record.setColDate(data.getColDate());
        record.setCreateTime(new Date());
        energyDataProcessManager.insertData(record);
        return ResultVO.success(record);
    }

    private OriginalDataDealConfigDTO getOriginalDataDealConfig() {
        List<SystemConfigDO> configs = systemConfigManager.selectByType(EmsConst.SystemConfigKey.SYSTEM_CONFIG_ORIGINAL_DATA_DEAL_CONFIG_KEY);
        if (CollectionUtil.isEmpty(configs)) {
            return new OriginalDataDealConfigDTO();
        }
        SystemConfigDO systemConfigDO = configs.get(0);
        OriginalDataDealConfigDTO bean = JSONUtil.toBean(JSONUtil.toJsonStr(systemConfigDO.getValue()), OriginalDataDealConfigDTO.class);
        bean.setSystemConfigId(systemConfigDO.getId());
        return bean;
    }

    @Override
    public void afterSingletonsInstantiated() {}
}
